# 策略类
import pandas as pd
import math
from stock import Bar


class Strategy:

    def trade1(self, Bars, day_count=5, K1=0.25, K2=0.3):
        '''
        该策略的思路：动态策略，初始建仓时对选定的股票给有相同的权值1，之后根据每日的情况调整权值，据此进行买卖交易；
                   HH是前n日最高价的最大值，LC是前n日收盘价的最小值，HC是前n日收盘价的最大值，LL是前n日最低价的最小值，open_price是前一日的开盘价；
                   range=max(HH-LC,HC-LL)，buyline=open_price+K1*range，sellline=open_price-K2*range；
                   当日的开盘价open>=buyline时，权值加1；当日的开盘价open<=sellline时，权值减1(最小值为0，因为中国股票市场不允许卖空)；
                   之后对每日的股票的权值进行标准化调整，使得每日权值总和为1，即为权重；
                   每日根据权重的变化进行调仓
        :param Bars: list，每个元素是Bar实例，包含所需股票的信息
        :param day_count: 策略中选定的n，默认是5
        :param K1: 策略中的参数，用于确定buyline，默认是0.45
        :param K2: 策略中的参数，用于确定sellline，默认是0.45
        :return: DataFrame，index是日期，columns是股票代码，内容是每日持有每只股票的权重
        '''
        weight0 = {}
        for stock in Bars:  # 选定排名前几的股票作为股票池
            datelist = stock.stock_data.index  # 得到所有的日期
            hh = stock.getMaxorMin(indicator="high", day_count=day_count, type=0)  # n日最高价的最大值，得到series
            lc = stock.getMaxorMin(indicator="close", day_count=day_count, type=1)  # n日收盘价的最小值，得到series
            hc = stock.getMaxorMin(indicator="close", day_count=day_count, type=0)  # n日收盘价的最大值，得到series
            ll = stock.getMaxorMin(indicator="low", day_count=day_count, type=1)  # n日最低价的最小值，得到series
            list = []  # 股票在每天的权值
            n = 0
            idx = 1  # 股票的初始权值
            last_day = datelist[0]
            for date in datelist:
                if n < day_count:
                    list.append(1)  # 最开始等比例持有
                elif hh[date] == float("nan") or lc[date] == float("nan") or hc[date] == float("nan") or ll[
                    date] == float("nan"):
                    list.append(idx)  # 表示停牌时权值不发生改变
                else:
                    HH = hh[date]
                    LC = lc[date]
                    HC = hc[date]
                    LL = ll[date]
                    if HH - LC >= HC - LL:
                        range = HH - LC
                    else:
                        range = HC - LL
                    open_price = stock.stock_data.loc[last_day, "open"]
                    buyline = open_price + K1 * range
                    sellline = open_price - K2 * range
                    price = stock.stock_data.loc[date, "open"]
                    if price >= buyline:
                        idx += 1  # 表示权值减1
                    elif price <= sellline:
                        if idx >= 1:
                            idx -= 1  # 表示权值减1
                        else:
                            idx = 0  # 表示权值最小为0
                    list.append(idx)
                last_day = date
                n += 1
            weight0[stock.code] = list
        df0 = pd.DataFrame(weight0, index=datelist.tolist())  # 得到内容为权值的DataFrame
        df1 = df0.sum(axis=1)  # 按行相加
        df = df0.div(df1, axis="rows")  # 得到内容为权重的DataFrame
        return df

    def trade2(self, Bars, day_count=3, up=0.02, down=0.02):
        '''
        该策略的思路：trade2是一个动态策略。第一步，选股并建仓。选取可选股票中夏普比率最高的stockNum支股票进行初始建仓，此时每只股票的权重相等，
        且权重和为1。第二步，构建策略。此策略的基本原理是，我们认为过去day_count天连续上涨的股票具备持续上涨的潜力，属于强势股，此时我们应该上调所
        持此股票的权重；而过去day_count天连续下跌的股票未来会继续下跌，属于弱势股，此时我们应该下调所持此股票的权重。每个交易日我们都重新判断所持
        股票是否为强势股或弱势股，且每次上调或下调2%的权重，最后对每天所持股票的权重进行标准化调整，使得每日权重总和为1。每天根据标准化后的权重进行调仓。

        :param Bars: list，每个元素是Bar实例，包含所需股票的信息
        :param day_count: 策略中观察前day_count的涨跌情况
        :param up: 策略中的参数，用于确定上调权重，默认是0.02
        :param down: 策略中的参数，用于确定下调权重，默认是0.02

        '''
        weight = {}
        for stock in Bars:  # 选夏普比率最高的前stockNum支股票并循环
            closePrice = stock.stock_data["close"]  # 一个股票的收盘价
            openPrice = stock.stock_data["open"]  # 一个股票的开盘价
            diffPrice = closePrice - openPrice  # 一个股票收盘价和开盘价的差值
            datelist = stock.stock_data.index.tolist()  # 一个股票的日期列表
            weight0 = 1 / len(Bars)  # 股票的初始权重
            weightList = []  # 权重列表
            for n in range(len(datelist)):
                if n < day_count:  # 前day_count天无法比较，则保持其初始权重
                    weightList.append(weight0)
                elif float("nan") in diffPrice[n - day_count:n]:  # 如果开盘价和收盘价中有"nan"值，则和昨天权重相等
                    weightList.append(weight0)
                else:
                    if diffPrice[n - day_count:n].all() > 0:  # 如果过去day_count天连续上涨，则权重上调up
                        weight0 = weight0 + up
                        weightList.append(weight0)
                    elif diffPrice[n - day_count:n].all() < 0:  # 如果过去day_count天连续下跌，则权重下调down
                        if weight0 - down < 0:  # 判断下调后的权重是否小于0，若小于0则权重设为0
                            weight0 = 0
                            weightList.append(weight0)
                        else:
                            weight0 = weight0 - down
                            weightList.append(weight0)
                    else:
                        weightList.append(weight0)
            weight[stock.code] = weightList
        df1 = pd.DataFrame(weight, index=datelist)  # 得到index为日期，columns为股票代码，内容为每天每只股票的权重
        df2 = df1.sum(axis=1)
        df = df1.div(df2, axis='rows')  # 标准化为最终权重DataFrame
        return df


